/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.sys>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: deployment_parser.cpp
 *
 * Purpose: deployment information parser implmentation
 *
 * Developer:
 *   wen.gu , 2021-04-29
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/

#include "deployment_parser.h"
#include "utils.h"
#include "key_word.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/


/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/



/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

static bool ParseNamespace(const YAML::Node& nd, NamespaceInfo& namespace_info)
{
    if (HasMember(nd, KW_NAMESPACE_FIRST))
    {
        if (!nd[KW_NAMESPACE_FIRST].IsScalar())
        {
            PrintError(nd, "the first namespace must be a scalar");
            return false;
        }
        namespace_info.frist = nd[KW_NAMESPACE_FIRST].as<std::string>();
    }

    if (HasMember(nd, KW_NAMESPACE_SECOND))
    {
         if (!nd[KW_NAMESPACE_SECOND].IsScalar())
        {
            PrintError(nd, "the second namespace must be a scalar");
            return false;
        }
        namespace_info.frist = nd[KW_NAMESPACE_SECOND].as<std::string>();       
    }

    return true;
}

static bool ParseMapElement(YAML::Node::const_iterator& it, MapElement& me)
{
    if (!it->first.IsScalar() || !it->second.IsScalar())
    {
        PrintError(it->first, "the both name and id must be scalar");
        return false;
    }

    if (!NodeIsType<uint32_t>(it->second))
    {
        PrintTypeError(it->second, "the element id: ", it->second.as<std::string>(), " must be a integer(uint32)");
        return false;
    }

    me.name = it->first.as<std::string>();
    me.id = it->second.as<uint32_t>();

    return true;
}

static bool ParseMapElementArray(const YAML::Node& nd, MapElementArray& mea)
{
    for (YAML::Node::const_iterator it = nd.begin(); it != nd.end(); ++it)
    {
        MapElement me;
        if (!ParseMapElement(it, me))
        {
            return false;
        }

        mea.push_back(std::move(me));
    }    

    return true;
}

static bool ParseMethodMap(const YAML::Node& nd, MethodMapInfo& method_map)
{
    if (!nd.IsMap())
    {
        PrintError(nd, "the method map must be a object");
        return false;
    }

    return ParseMapElementArray(nd, method_map.method_maps);
}

static bool ParseOneEventGroup(const YAML::Node& nd, EventGroupInfo& egi)
{
    if (!HasMember(nd, KW_GROUP_ID))
    {
        PrintError(nd, "the event gorup must have key word: _group_id");
        return false;            
    }

    if (!NodeIsType<uint32_t>(nd[KW_GROUP_ID]))
    {
        PrintTypeError(nd[KW_GROUP_ID], "the group id: ", nd[KW_GROUP_ID].as<std::string>(), " must be a integer(uint32)");
        return false;       
    }

    egi.info.id = nd[KW_GROUP_ID].as<uint32_t>();

    if (!HasMember(nd, KW_ELEMENT))
    {
        PrintError(nd, "the event map in a group must have key word: _element");
        return false;            
    }
    
    return ParseMapElementArray(nd[KW_ELEMENT], egi.event_maps);
}

static bool ParseEventMap(const YAML::Node& nd, EventMapInfo& event_map)
{
    if (!nd.IsMap())
    {
        PrintError(nd, "the event map must be a object");
        return false;
    }

    for (YAML::Node::const_iterator it = nd.begin(); it != nd.end(); ++it)
    {
        if (!it->first.IsScalar() || !it->second.IsMap())
        {
            PrintError(nd, "the group name must be scalar, the group content must be object");
            return false;
        }
        EventGroupInfo egi;
        egi.info.name = it->first.as<std::string>();

        if (!ParseOneEventGroup(it->second, egi))
        {
            return false;
        }

        event_map.event_groups.push_back(std::move(egi));
    }

    return true;
}


static bool ParsePropertyMap(const YAML::Node& nd, PropertyMapInfo& property_map)
{
    if (!nd.IsMap())
    {
        PrintError(nd, "the property map must be a object");
        return false;
    }

    return ParseMapElementArray(nd, property_map.property_maps);
}

static bool ParseCommunicationBinding(const YAML::Node& nd, CommunicationBiding& binding)
{
    if (!HasMember(nd, KW_BINDING_PROTOCOL) || !HasMember(nd, KW_BINDING_SER_DESER))
    {
        PrintError(nd, "The communication binding content node must be  include both _protocol and _serializer_deserializer");
        return false;
    }

    if ((!NodeIsType<std::string>(nd[KW_BINDING_PROTOCOL])) || (!NodeIsType<std::string>(nd[KW_BINDING_SER_DESER])))
    {
         PrintError(nd, "The _protocol and _serializer_deserializer must be scalar and string");
        return false;       
    }

    binding.protocol = nd[KW_BINDING_PROTOCOL].as<std::string>();
    binding.serializer_deserializer = nd[KW_BINDING_SER_DESER].as<std::string>();

    return true;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


DeploymentParser::DeploymentParser()
    :ContentParser(ContentType::kDeployment)
{
    /** todo something */
}

DeploymentParser::~DeploymentParser()
{
    /** todo something */
}


bool DeploymentParser::parse(const YAML::Node& content_node, FileContent& file_content)
{
    if (!content_node.IsMap())
    {
        PrintError(content_node, "The deployment content node must be a object");
        return false;
    }

    DeploymentInfo& deployment_info = file_content.deployment_info;

    if (!HasMember(content_node, KW_COMMUNICATION_BINDING))
    {
        PrintError(content_node, "The deployment content node must be have communication binding information ");
        return false;
    }

    if (!ParseCommunicationBinding(content_node[KW_COMMUNICATION_BINDING], deployment_info.binding_info))
    {
        return false;
    }

    if (HasMember(content_node, KW_NAMESPACE))
    {
        if (!ParseNamespace(content_node[KW_NAMESPACE], deployment_info.namespace_info))
        {
            return false;
        }
    }   

    if (HasMember(content_node, KW_METHOD_MAP))
    {
        if (!ParseMethodMap(content_node[KW_METHOD_MAP], deployment_info.method_map))
        {
            return false;
        }
    }
    if (HasMember(content_node, KW_EVENT_MAP))
    {
        if (!ParseEventMap(content_node[KW_EVENT_MAP], deployment_info.event_map))
        {
            return false;
        }
    }

    if (HasMember(content_node, KW_PROPERTY_MAP))
    {
        if (!ParsePropertyMap(content_node[KW_PROPERTY_MAP], deployment_info.property_map))
        {
            return false;
        }
    }

    return true;
}




